﻿using System;
using System.Collections.Generic;
using UnityEngine;
using System.Collections;

[ExecuteInEditMode, RequireComponent(typeof(MeshFilter)), RequireComponent(typeof(MeshRenderer))]
public class TrainResPipeLineCore : MonoBehaviour
{
    public Texture mainTexture;
    List<LineCircle> circles = new List<LineCircle>();
    List<LineCircle> renders = new List<LineCircle>();
    public List<Transform> markers = new List<Transform>();

    public float pipeLineRadius = 1.0f;
    int divPoints = 6;
    public bool showAnimation = false;//是否播放动画
    float animationPos = 0;//当前流程播放长度，单位m
    public float animationSpeed = 5;//动画播放速度，在没有指定时间的情况下，单位m/s
    float animationTime = 0;//动画开始播放的时间
    public bool visible = true;//动画开始播放的时间
    public float pauseTime = 0;
    public int renderType = 0;
    public float uvStep = 5;
    public float cornerRadius = 3.0f;
	LineCircle interposelc = new LineCircle();
	
	private bool needRebuild = true;

    static public bool showBlink = false;
    public Color[] blinkColors = new Color[]{ new Color(255,0,0), new Color(0,255,0) };
    public float blinkEscape = 0.5f;
    private int blinkColorIndex = 0;
    private float lastTime = 0f;

    void Start()
    {
    }
	
	void Update()
	{
        if (markers.Count < 2) return;

		if(needRebuild)
		{
			needRebuild = false;
			if(pipeLineRadius<0.3)
				divPoints = 6;
			else if(pipeLineRadius<0.6)
				divPoints = 8;
			else
				divPoints = 12;

			rebuildPipeMesh();//计算拐点圆
            if (!showAnimation)
            {
                buildRenderMesh(1e10f);//计算需要渲染的拐点圆
                if (renderType == 0)
                    renderPipeLine();//生成Mesh
                else
                    renderStrip();
            }
		}
		
		if(showAnimation)
		{
			animationTime += Time.deltaTime;
			if(pauseTime<0.0001)
			{
				animationPos += animationSpeed * Time.deltaTime;
				buildRenderMesh(animationPos);
				if(renderType==0)
					renderPipeLine();
				else
					renderStrip();
			}
			else
			{
				pauseTime -= Time.deltaTime;
			}
		}

        //Debug.Log(renderer.material.color);
        if (showBlink)
        {
            if (Time.time - lastTime > blinkEscape)
            {
                lastTime = Time.time;
                blinkColorIndex++;
                if (blinkColorIndex >= blinkColors.Length)
                    blinkColorIndex = 0;
                if (blinkColorIndex < blinkColors.Length)
                {
                    Color c = blinkColors[blinkColorIndex];
                    renderer.material.color = c;
                }
            }
        }
        else
        {
            if (renderer.material.color != Color.white)
                renderer.material.color = Color.white;
        }
	}
	
	public void playAnimation(float pos)
	{
		showAnimation = true;
		animationPos = pos;
	}
	
	public float getAnimationTime()
	{
		return animationTime;
	}
	
	public void refresh()
	{
		needRebuild = true;
	}
	
	public Vector3 getFlowPoint()
	{
		return interposelc.pos;
	}

    void buildRenderMesh(float pos)
    {
        if (circles.Count < 2)
            return;

        renders.Clear();
        float currpos = 0;
        renders.Add(circles[0]);
        if (showAnimation)
        {
            //Con::executef(this, "OnAnimationNode", Con::getIntArg(interposelc.marker));
        }

        for (int i = 0; i < circles.Count - 1; i++)
        {
            LineCircle lc = circles[i];
            LineCircle next = circles[i + 1];

            Vector3 v = next.pos - lc.pos;
            float d = v.magnitude;
            v.Normalize();
            if (currpos + d > pos)
            {
                float interpose = (pos - currpos) / d;
                Vector3 p = lc.pos + v * d * interpose;
                //if ((v - lc.normal).magnitude < 0.0001)
                {
                    interposelc.pos = p;
                    interposelc.normal = lc.normal;
                    interposelc.radiusV = lc.radiusV;
                }
                /*else
                {
                    //拐角圆法线
                    Vector3 planenormal = Vector3.Cross(lc.radiusV, lc.normal);
                    //拐角圆心
                    Vector3 center = lc.pos + lc.radiusV * lc.radius;
                    //求插值圆心
                    Vector3 vp = p - center;
                    vp = vp.normalized * lc.radius;

                    //得到圆心位置
                    interposelc.pos = center + vp;
                    interposelc.normal = Vector3.Cross(planenormal, vp);
                    interposelc.normal.Normalize();
                    interposelc.radiusV = vp;
                }*/

                interposelc.uvpos = currpos + d * interpose;
                interposelc.radius = lc.radius;
                generateCirclePoint(interposelc);
                renders.Add(interposelc);
                break;
            }
            else
            {
                renders.Add(next);
                if (showAnimation)
                {
                    //if(next.marker != lc.marker)
                    //	Con::executef(this, "OnAnimationNode", Con::getIntArg(next.marker));
                }
                currpos += d;
                if (i + 1 >= circles.Count - 1)
                {
                    //if(showAnimation)
                    //	Con::executef(this, "OnAnimationEnd");
                    showAnimation = false;
                    animationPos = 0;
                    animationTime = 0;
                }
            }
        }
    }

    Vector3 RadiusV(Vector3 y1, Vector3 y2, Vector3 x1)
    {
        Quaternion q = Quaternion.FromToRotation(y2, y1);
        Vector3 x2 = q * x1;
        return x2;
    }

    void generateCirclePoint(LineCircle circle)
    {
        circle.points.Clear();
        for (int j = 0; j < divPoints; j++)
        {
            float delta = (float)(360f / divPoints * j);
            Vector3 r = circle.radiusV;
            Quaternion q = Quaternion.AngleAxis(delta, circle.normal);
            Vector3 p = q * r;
            p = p.normalized * circle.radius;
            p += circle.pos;

            circle.points.Add(p);
        }
    }

    void rebuildPipeMesh()
    {
        if (mainTexture != null)
        {
            this.renderer.material.mainTexture = mainTexture;
        }

        circles.Clear();
        //专家意见，拐角半径是管半径3倍
        float radius = pipeLineRadius * 3;
        if (cornerRadius > 0.001)//如果设定了拐角半径就以设定的为拐角半径
            radius = cornerRadius;
        Vector3 start = markers[0].localPosition;//起始点

		LineCircle lc = new LineCircle();
        for (int i = 1; i < markers.Count; i++)
        {
            lc.radius = pipeLineRadius;//记录管道半径
            lc.marker = markers[i - 1];//记录marker

            Transform obj = markers[i];//取出下一个marker
            Vector3 end = obj.localPosition;//下一个marker的位置
            if (i > 1)
            {
                Transform prevM = markers[i - 2];
                Vector3 prev = prevM.localPosition;
                //计算拐角各个点
                Vector3 v1 = prev - start;//夹角向量
                Vector3 v2 = end - start;//夹角向量
                //求平面法线
                Vector3 planenormal = Vector3.Cross(v1, v2);
                planenormal.Normalize();
                //求夹角
                float alpha = (float)Math.Acos(Vector3.Dot(v1, v2) / (v1.magnitude * v2.magnitude));
                float vlen = (float)(radius / Math.Tan(alpha / 2));//start点到切点的距离
                v1 = v1.normalized * vlen;//得到start指向切点的向量
                v2 = v2.normalized * vlen;//得到start指向切点的向量
                //从start点指向圆心的向量
                Vector3 v = v1;
                v.Normalize();
                Vector3 cutpointv = Vector3.Cross(planenormal, v) * radius;//切点到圆心的向量
                Vector3 centerv = v1 + cutpointv;
                //世界坐标系圆心位置
                Vector3 center = start + centerv;
                //世界坐标系切点
                Vector3 cutpoint1 = start + v1;
                Vector3 cutpoint2 = start + v2;
                //求拐角各个点
                float rot = (float)(3.14159 - alpha)*Mathf.Rad2Deg;
                for (float r = 0; r < rot + 0.001; )
                {
                    Matrix4x4 m = Matrix4x4.identity;
                    Quaternion q = Quaternion.AngleAxis(r, -planenormal);
                    Vector3 rp = q * -cutpointv;

                    //拐点位置
                    Vector3 p1 = center + rp;
                    //拐点切线
                    rp.Normalize();
                    Vector3 p2 = Vector3.Cross(planenormal, -rp);
                    p2.Normalize();

                    lc.pos = p1;
                    lc.normal = p2;

                    LineCircle prev_lc = circles[circles.Count - 1];
                    lc.radiusV = RadiusV(prev_lc.normal, lc.normal, prev_lc.radiusV);
                    lc.uvpos = prev_lc.uvpos + (lc.pos - prev_lc.pos).magnitude;

                    circles.Add(new LineCircle(lc));
                    r += rot / 9.0f;
                }
            }
            else
            {
                //处理第一个点的圆
                lc.normal = end - start;//线圈法线
                lc.normal.Normalize();//法线归一
                lc.radiusV = RadiusV(Vector3.up, lc.normal, Vector3.left);//计算指向拐心的向量
                lc.pos = start;//记录圆心的位置
                lc.uvpos = 0;

                circles.Add(new LineCircle(lc));
            }
            start = end;
        }
        //处理最后一个点
        Transform lastMarker = markers[markers.Count - 1];
        LineCircle llc = new LineCircle(circles[circles.Count - 1]);
        llc.uvpos += (start - llc.pos).magnitude;
        llc.pos = start;
        llc.marker = lastMarker;

        circles.Add(llc);

        //生成圆环上的点，每圆6点
        for (int i = 0; i < circles.Count; i++)
        {
            LineCircle circle = circles[i];
            generateCirclePoint(circle);
        }
    }

    int renderPort(Vector3[] verts, Vector2[] uvs, int[] triangles, int startindex, LineCircle lc, bool clockwise)
	{
		int index = 0;
		for(int i=0; i<lc.points.Count - 2; i++)
		{
			verts[startindex + index + 0] = clockwise?lc.points[0]:lc.points[i+2];
			verts[startindex + index + 1] = lc.points[i+1];
			verts[startindex + index + 2] = clockwise?lc.points[i+2]:lc.points[0];
			uvs[startindex + index + 0] = new Vector2(0, 0);
			uvs[startindex + index + 1] = new Vector2(1, 0);
			uvs[startindex + index + 2] = new Vector2(1, 1);
			triangles[startindex + index + 0] = startindex + index + 0;
			triangles[startindex + index + 1] = startindex + index + 1;
			triangles[startindex + index + 2] = startindex + index + 2;
			index += 3;
		}
		return startindex + index;
	}
	
	void renderPipeLine()
    {
		int addition = 3 * (divPoints - 2) * 2;//管道两端封口顶点数
        int count = renders.Count - 1;
		int pointcount = count * 6 * divPoints + addition;

        Vector3[] verts = new Vector3[pointcount];
		Vector2[] uvs = new Vector2[pointcount];
		int[] triangles = new int[pointcount];
		
        int index = 0;
        for (int i = 0; i < count; i++)
        {
            LineCircle circle1 = renders[i];
            LineCircle circle2 = renders[i + 1];
            if (circle1.points.Count != 0 && circle2.points.Count != 0)
            {
                List<Vector3> ps1 = circle1.points;
                List<Vector3> ps2 = circle2.points;
                for (int j = 0; j < divPoints; j++)
                {
                    index = i * 6 * divPoints + j * 6;
                    int k = j + 1;
                    if (k >= divPoints) k = 0;

                    Vector3 p1 = ps1[j];
                    Vector3 p2 = ps2[j];
                    Vector3 p3 = ps2[k];
                    Vector3 p4 = ps1[k];

                    verts[index+0] = p3;
                    verts[index+1] = p2;
                    verts[index+2] = p1;

                    verts[index+3] = p4;
                    verts[index+4] = p3;
                    verts[index+5] = p1;

                    float u1 = circle1.uvpos / uvStep;
                    float u2 = circle2.uvpos / uvStep;
                    float v1 = j / (float)divPoints;
                    float v2 = (j + 1) / (float)divPoints;
                    uvs[index+0] = new Vector2(u2,v2);
                    uvs[index+1] = new Vector2(u2,v1);
                    uvs[index+2] = new Vector2(u1,v1);
                    uvs[index+3] = new Vector2(u1,v2);
                    uvs[index+4] = new Vector2(u2,v2);
                    uvs[index+5] = new Vector2(u1,v1);
					
					triangles[index+0] = index+0;
					triangles[index+1] = index+1;
					triangles[index+2] = index+2;
					triangles[index+3] = index+3;
					triangles[index+4] = index+4;
					triangles[index+5] = index+5;
                }
            }
        }
		
		index += 6;
		index = renderPort(verts, uvs, triangles, index, renders[0], false);
		renderPort(verts, uvs, triangles, index, renders[renders.Count-1], true);
		
		Mesh mesh = GetComponent<MeshFilter>().mesh;
		mesh.vertices = verts;
		mesh.uv = uvs;
		mesh.triangles = triangles;
    }

    void renderStrip()
    {
        int count = renders.Count - 1;

        Vector3[] verts = new Vector3[count * 6];
		Vector2[] uvs = new Vector2[count * 6];
		int[] triangles = new int[count * 6];
		
        int index = 0;
        for (int i = 0; i < count; i++)
        {
            LineCircle circle1 = renders[i];
            LineCircle circle2 = renders[i + 1];
            if (circle1.points.Count != 0 && circle2.points.Count != 0)
            {
                List<Vector3> ps1 = circle1.points;
                List<Vector3> ps2 = circle2.points;

                int div = divPoints / 2;
                index = i * 6;
                verts[index+0] = ps1[0];
                verts[index+1] = ps1[div];
                verts[index+2] = ps2[div];
                verts[index+3] = ps1[0];
                verts[index+4] = ps2[div];
                verts[index+5] = ps2[0];

                float u1 = circle1.uvpos / uvStep;
                float u2 = circle2.uvpos / uvStep;
                uvs[index+0] = new Vector2(u1, 0);
                uvs[index+1] = new Vector2(u1, 1);
                uvs[index+2] = new Vector2(u2, 1);
                uvs[index+3] = new Vector2(u1, 0) ;
                uvs[index+4] = new Vector2(u2, 1);
                uvs[index+5] = new Vector2(u2, 0);
				
				triangles[index+0] = index+0;
				triangles[index+1] = index+1;
				triangles[index+2] = index+2;
				triangles[index+3] = index+3;
				triangles[index+4] = index+4;
				triangles[index+5] = index+5;
            }
        }
		
		Mesh mesh = GetComponent<MeshFilter>().mesh;
		mesh.vertices = verts;
		mesh.uv = uvs;
		mesh.triangles = triangles;
    }
}

public class LineCircle
{
    public Vector3 normal;//法线
    public Vector3 pos;//圆心
    public Vector3 radiusV;//指向拐心的向量
    public float radius;//半径
    public float uvpos;//uv位置
    public Transform marker;//所属标记

    public List<Vector3> points;//生成的点
	
	public LineCircle()
	{
		points = new List<Vector3>();
	}
	
	public LineCircle(LineCircle lc)
	{
		normal = lc.normal;
		pos = lc.pos;
		radiusV = lc.radiusV;
		radius = lc.radius;
		uvpos = lc.uvpos;
		marker = lc.marker;
		points = new List<Vector3>();
	}
};
